from trac.core import *
from trac.util import Markup
from trac.web.chrome import INavigationContributor
from trac.web.chrome import ITemplateProvider
from trac.web import IRequestHandler
from trac.Timeline import ITimelineEventProvider
from trac.Search import ISearchSource
import re
from pkg_resources import resource_filename
from datetime import datetime, timedelta
from time import mktime
import urllib
import Cookie
import feedparser
import xmltramp

try:
    set
except:
    from sets import Set as set

class storage(dict):
    def __getattr__(self, key): 
        try:
            return self[key]
        except KeyError, k:
            raise AttributeError, k

    def __setattr__(self, key, value): 
        self[key] = value

class Bugzilla:
    def __init__(self, base, user, password, **kwargs):
        self.base = base
        self.user = user
        self.password = password
        self.defaults = kwargs

    def _urlopen(self, action, data=None, **kwargs):
        data = data or urllib.urlencode(kwargs)
        cookie = Cookie.SimpleCookie(urllib.urlopen(self.base + 'index.cgi',
                                                    urllib.urlencode(dict(Bugzilla_login=self.user,
                                                                          Bugzilla_password=self.password,
                                                                          GoAheadAndLogin='1'))).headers['set-cookie'])
        opener = urllib.URLopener()
        opener.addheader('Cookie', cookie.output(attrs=[], header='').strip())
        return opener.open(self.base + action + '.cgi', data).read()

    def _buglist(self, **kwargs):
        kwargs['ctype'] = 'atom'
        return feedparser.parse(self._urlopen('buglist', **kwargs))

    def _bugs(self, ids):
        data = urllib.urlencode([('ctype', 'xml')] +
                                [('id', id) for id in ids])
        return xmltramp.parse(self._urlopen('show_bug', data))

    def search(self, **kwargs):
        params = dict()
        params.update(self.defaults)
        params.update(kwargs)
        xml = self._bugs([entry.id.split('=')[1] for entry \
                          in self._buglist(**params).entries])
        for xml_bug in xml['bug':]:
            bug = storage()
            bug.id = str(xml_bug['bug_id'])
            bug.title = str(xml_bug['short_desc'])
            bug.opened = feedparser._parse_date(str(xml_bug['creation_ts']).replace(' ', 'T'))
            bug.changed = feedparser._parse_date(str(xml_bug['delta_ts']).replace(' ', 'T'))
            bug.product = str(xml_bug['product'])
            bug.component = str(xml_bug['component'])
            bug.version = str(xml_bug['version'])
            bug.platform = str(xml_bug['rep_platform'])
            bug.os = str(xml_bug['op_sys'])
            bug.status = str(xml_bug['bug_status'])
            try:
                bug.resolution = str(xml_bug['resolution'])
            except:
                bug.resolution = ''
            bug.priority = str(xml_bug['priority'])
            bug.severity = str(xml_bug['bug_severity'])
            bug.milestone = str(xml_bug['target_milestone'])
            bug.reporter = str(xml_bug['reporter'])
            bug.assigned_to = str(xml_bug['assigned_to'])
            bug.qa = str(xml_bug['qa_contact'])
            bug.cc = [str(cc) for cc in xml_bug['cc':]]
            bug.comments = [storage(when=feedparser._parse_date(str(xml_long_desc['bug_when']).replace(' ', 'T')),
                                    who=str(xml_long_desc['who']),
                                    text=str(xml_long_desc['thetext'])) \
                            for xml_long_desc in xml_bug['long_desc':]]
            bug.url = '%sshow_bug.cgi?id=%s' % (self.base, bug.id)
            yield bug

class BugzillaPlugin(Component):
    implements(INavigationContributor,
               IRequestHandler,
               ITemplateProvider,
               ITimelineEventProvider,
               ISearchSource)

    def __init__(self, *args, **kwargs):
        self.base = self.config.get('bugzilla', 'base', None)
        self.bugzilla = Bugzilla(self.base,
                                 self.config.get('bugzilla', 'user', None),
                                 self.config.get('bugzilla', 'password', None),
                                 product=self.config.get('bugzilla', 'product', None))
        
        Component.__init__(self, *args, **kwargs)

    # INavigationContributor methods

    def get_active_navigation_item(self, req):
        return 'bugs'

    def get_navigation_items(self, req):
        yield ('mainnav',
               'bugs',
               Markup('<a href="%s">Bugs</a>', self.env.href.bugs()))
               
    # IRequestHandler methods

    def match_request(self, req):
        return (re.match(r'/bugs/?', req.path_info) is not None)

    def process_request(self, req):
        req.hdf['bugs.list'] = list(self.bugzilla.search())
        return 'bugs.cs', None

    # ITimelineEventProvider methods

    def get_timeline_filters(self, req):
        yield ('bugs', 'Bugs', True)

    def get_timeline_events(self, req, start, stop, filters):
        def _format_date(date):
            return '%s-%s-%s' % (date.year, date.month, date.day)
            
        if 'bugs' in filters:
            for bug in self.bugzilla.search(chfieldfrom=_format_date(datetime.fromtimestamp(start)),
                                            chfieldto=_format_date(datetime.fromtimestamp(stop) +
                                                                   timedelta(days=1))):
                yield ('bug',
                       '%sshow_bug.cgi?id=%s' % (self.base, bug.id),
                       'Bug #%(id)s: %(title)s' % bug,
                       mktime(bug.changed),
                       bug.reporter,
                       Markup('%(assigned_to)s %(status)s %(resolution)s' % bug))

    # ITemplateProvider methods

    def get_htdocs_dirs(self):
        yield resource_filename(__name__, 'htdocs')

    def get_templates_dirs(self):
        yield resource_filename(__name__, 'templates')

    # ISearchSource methods
    
    def get_search_filters(self, req):
        yield ('bugs', 'Bugs', True)
    
    def get_search_results(self, req, terms, filters):
        results = []
        if 'bugs' in filters:
            all_bugs = (list(self.bugzilla.search(short_desc=terms, short_desc_type='allwordssubstr')) +
                        list(self.bugzilla.search(long_desc=terms, long_desc_type='allwordssubstr')))
            bugids = set([bug.id for bug in all_bugs])
            bugs_list = []
            for bugid in bugids:
                for bug in all_bugs:
                    if bug.id == bugid:
                        bugs_list.append(bug)
                        break
            for bug in bugs_list:
                href = '%sshow_bug.cgi?id=%s' % (self.base, bug.id)
                title = 'Bug #%(id)s: %(title)s' % bug
                date = mktime(bug.changed)
                author = bug['reporter']
                excerpt = '%(assigned_to)s %(status)s %(resolution)s' % bug
                results.append((href, title, date, author, excerpt))
        return results
